function TourMap(element, mapOptions, eventInfoCallback) {
	this.map = new google.maps.Map($(element).get(0), mapOptions);
	//this.infoWindow = null;
	this.eventInfoCallback = eventInfoCallback;
	this.mapBounds = new google.maps.LatLngBounds();

	this.airports = [];
	this.pointsKeys = [];
	this.points = {};

	this.shadowPaths = [];
	this.carPaths = [];
	this.flightPaths = [];
}

TourMap.prototype.MARKERS_PATH          = STATIC_PREFIX + "images/";
TourMap.prototype.AIRPORT_ICON          = TourMap.prototype.MARKERS_PATH + "airport.png";
TourMap.prototype.EVENT_ICON            = TourMap.prototype.MARKERS_PATH + "event.png";
TourMap.prototype.POI_ICON              = TourMap.prototype.MARKERS_PATH + "poi.png";
TourMap.prototype.HOTEL_ICON            = TourMap.prototype.MARKERS_PATH + "hotel.png";

TourMap.prototype._createMarker = function(lat, lon, title, icon, draggable) {
	if(draggable == undefined)
		draggable = false;

	return new google.maps.Marker({
		position: new google.maps.LatLng(lat, lon),
		map: this.map,
		title: title,
		icon: icon,
		draggable: draggable
	});
}

TourMap.prototype._createPolyline = function(points, color, opacity, geodesic) {
	if(geodesic == undefined)
		geodesic = false;

	var latLngPoints = [];
	for(var i=0; i<points.length-1; i += 2) {
		latLngPoints.push(new google.maps.LatLng(points[i], points[i+1]));
	}

	return new google.maps.Polyline({
		map: this.map,
		path: latLngPoints,
		strokeColor: color,
		strokeOpacity: opacity,
		strokeWeight: 3,
		geodesic: geodesic
	});
}

TourMap.prototype._createCircle = function(lat, lon, color) {
	return new google.maps.Circle({
		strokeColor: color,
		strokeOpacity: 0.95,
		strokeWeight: 2,
		fillColor: color,
		fillOpacity: 0.5,
		map: this.map,
		center: new google.maps.LatLng(lat, lon),
		radius: 140000
	});
}

TourMap.prototype._generateKey = function(api, id) {
	return api + "::" + id;
}

TourMap.prototype.addPoint = function(type, obj) {
	var name = obj.name;
	var pos = [obj.lat, obj.lon];
	var marker = this._createMarker(pos[0], pos[1], name, this.AIRPORT_ICON);

	if(type == "AIRPORT") {
		marker.setTitle(marker.getTitle() + " (" + obj.code + ")");
		this.airports.push(marker);
	}
	else {
		if(type == "EVENT") {
			marker.setIcon(this.EVENT_ICON);
		}
		else if(type == "POI") {
			marker.setIcon(this.POI_ICON);
		}
		else if(type == "HOTEL") {
			marker.setIcon(this.HOTEL_ICON);
		}

		var key = this._generateKey(obj.api, obj.id);

		var _this = this;
		google.maps.event.addListener(marker, 'click', function() {
			_this.showInfo(obj.api, obj.id);
		});

		this.pointsKeys.push(key);
		this.points[key] = {
			object: $.extend({}, obj),
			marker: marker
		};
	}
}

TourMap.prototype.addPath = function(type, obj) {
	if(type == "CAR") {
		var polyline = this._createPolyline(obj.shape, "#00aaff", 0.6);
		this.carPaths.push(polyline);
	}
	else if(type == "FLIGHT") {
		var shadowPolyline = this._createPolyline(obj.shape, "#000000", 0.05);
		this.shadowPaths.push(shadowPolyline);

		var polyline = this._createPolyline(obj.shape, "#0055ff", 0.6, true);
		this.flightPaths.push(polyline);
	}
}

TourMap.prototype.showInfo = function(api, id) {
	var key = this._generateKey(api, id);

	if(this.points[key]) {
		this.eventInfoCallback(api, id);
		/*
		if(this.infoWindow) {
			this.infoWindow.close();
			delete this.infoWindow;
		}

		var _this = this;
		this.infoWindow = new google.maps.InfoWindow({
			position: _this.points[key].marker.getPosition(),
			content: key
		});
		this.infoWindow.open(this.map);
		*/
	}
	else {
		console.log("TourMap::showInfo(api, id): Error. Invalid 'api' or 'id'.");
	}
}

TourMap.prototype.cleanTour = function() {
	for(var i=0; i<this.pointsKeys.length; i++) {
		this.points[this.pointsKeys[i]].marker.setMap(null);
		delete this.points[this.pointsKeys[i]];
	}
	delete this.pointsKeys;

	for(var i=0; i<this.carPaths.length; i++) {
		this.carPaths[i].setMap(null);
	}
	delete this.carPaths;

	for(var i=0; i<this.flightPaths.length; i++) {
		this.flightPaths[i].setMap(null);
		this.shadowPaths[i].setMap(null);
	}
	delete this.flightPaths;
	delete this.shadowPaths;

	this.pointsKeys = [];
	this.points = {};
	this.carPaths = [];
	this.flightPaths = [];
	this.shadowPaths = [];
}

TourMap.prototype.makeTour = function(json) {
	this.cleanTour();
	var numPoints = 0;
	var center = [0, 0];
	this.mapBounds = new google.maps.LatLngBounds();
	for(var i=0; i<json.length; i++) {
		if(json[i].type[0] == "POINT") {
			this.addPoint(json[i].type[1], json[i]);
			if(json[i].type[1] != "AIRPORT") {
				center[0] += json[i].lat;
				center[1] += json[i].lon;
				numPoints++;
				this.mapBounds.extend(new google.maps.LatLng(json[i].lat, json[i].lon));
			}
		}
		else if(json[i].type[0] == "TRAVEL") {
			this.addPath(json[i].type[1], json[i]);
		}
	}

	if(numPoints > 0) {
		center[0] /= numPoints;
		center[1] /= numPoints;
		this.mapCenter = new google.maps.LatLng(parseFloat(center[0]), parseFloat(center[1]));
		this.center();
	}
}

TourMap.prototype.center = function() {
	google.maps.event.trigger(this.map, 'resize');
	//this.map.setCenter(this.mapCenter);
	this.map.fitBounds(this.mapBounds);
	console.log(this.mapBounds.getNorthEast() + " " + this.mapBounds.getSouthWest());
	//this.map.panTo(this.mapCenter);
	google.maps.event.trigger(this.map, 'resize');
}

TourMap.prototype.addMapListener = function(eventName, callback) {
	google.maps.event.addListener(this.map, eventName, callback);
}
